1 /*
2 * Copyright (C) 2007 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.google.common.base;
18
19 import com.google.common.annotations.VisibleForTesting;
20
21 import java.io.Closeable;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.lang.ref.PhantomReference;
25 import java.lang.ref.Reference;
26 import java.lang.ref.ReferenceQueue;
27 import java.lang.reflect.Method;
28 import java.net.URL;
29 import java.net.URLClassLoader;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
32
33 import javax.annotation.Nullable;
34
35 /**
36 * A reference queue with an associated background thread that dequeues references and invokes
37 * {@link FinalizableReference#finalizeReferent()} on them.
38 *
39 * <p>Keep a strong reference to this object until all of the associated referents have been
40 * finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code
41 * finalizeReferent()} on the remaining references.
42 *
43 * <p>As an example of how this is used, imagine you have a class {@code MyServer} that creates a
44 * a {@link java.net.ServerSocket ServerSocket}, and you would like to ensure that the
45 * {@code ServerSocket} is closed even if the {@code MyServer} object is garbage-collected without
46 * calling its {@code close} method. You <em>could</em> use a finalizer to accomplish this, but
47 * that has a number of well-known problems. Here is how you might use this class instead:
48 *
49 * <pre>
50 * public class MyServer implements Closeable {
51 * private static final FinalizableReferenceQueue frq = new FinalizableReferenceQueue();
52 * // You might also share this between several objects.
53 *
54 * private static final Set<Reference<?>> references = Sets.newConcurrentHashSet();
55 * // This ensures that the FinalizablePhantomReference itself is not garbage-collected.
56 *
57 * private final ServerSocket serverSocket;
58 *
59 * private MyServer(...) {
60 * ...
61 * this.serverSocket = new ServerSocket(...);
62 * ...
63 * }
64 *
65 * public static MyServer create(...) {
66 * MyServer myServer = new MyServer(...);
67 * final ServerSocket serverSocket = myServer.serverSocket;
68 * Reference<?> reference = new FinalizablePhantomReference<MyServer>(myServer, frq) {
69 * @Override public void finalizeReferent() {
70 * references.remove(this):
71 * if (!serverSocket.isClosed()) {
72 * ...log a message about how nobody called close()...
73 * try {
74 * serverSocket.close();
75 * } catch (IOException e) {
76 * ...
77 * }
78 * }
79 * }
80 * };
81 * references.add(reference);
82 * return myServer;
83 * }
84 *
85 * @Override public void close() {
86 * serverSocket.close();
87 * }
88 * }
89 * </pre>
90 *
91 * @author Bob Lee
92 * @since 2.0 (imported from Google Collections Library)
93 */
94 public class FinalizableReferenceQueue implements Closeable {
95 /*
96 * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a
97 * map built by MapMaker) no longer has a strong reference to this object, the garbage collector
98 * will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the
99 * Finalizer to stop.
100 *
101 * If this library is loaded in the system class loader, FinalizableReferenceQueue can load
102 * Finalizer directly with no problems.
103 *
104 * If this library is loaded in an application class loader, it's important that Finalizer not
105 * have a strong reference back to the class loader. Otherwise, you could have a graph like this:
106 *
107 * Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application class loader
108 * which loaded -> ReferenceMap.class which has a static -> FinalizableReferenceQueue instance
109 *
110 * Even if no other references to classes from the application class loader remain, the Finalizer
111 * thread keeps an indirect strong reference to the queue in ReferenceMap, which keeps the
112 * Finalizer running, and as a result, the application class loader can never be reclaimed.
113 *
114 * This means that dynamically loaded web applications and OSGi bundles can't be unloaded.
115 *
116 * If the library is loaded in an application class loader, we try to break the cycle by loading
117 * Finalizer in its own independent class loader:
118 *
119 * System class loader -> Application class loader -> ReferenceMap -> FinalizableReferenceQueue
120 * -> etc. -> Decoupled class loader -> Finalizer
121 *
122 * Now, Finalizer no longer keeps an indirect strong reference to the static
123 * FinalizableReferenceQueue field in ReferenceMap. The application class loader can be reclaimed
124 * at which point the Finalizer thread will stop and its decoupled class loader can also be
125 * reclaimed.
126 *
127 * If any of this fails along the way, we fall back to loading Finalizer directly in the
128 * application class loader.
129 */
130
131 private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName());
132
133 private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer";
134
135 /** Reference to Finalizer.startFinalizer(). */
136 private static final Method startFinalizer;
137 static {
138 Class<?> finalizer = loadFinalizer(
139 new SystemLoader(), new DecoupledLoader(), new DirectLoader());
140 startFinalizer = getStartFinalizer(finalizer);
141 }
142
143 /**
144 * The actual reference queue that our background thread will poll.
145 */
146 final ReferenceQueue<Object> queue;
147
148 final PhantomReference<Object> frqRef;
149
150 /**
151 * Whether or not the background thread started successfully.
152 */
153 final boolean threadStarted;
154
155 /**
156 * Constructs a new queue.
157 */
158 public FinalizableReferenceQueue() {
159 // We could start the finalizer lazily, but I'd rather it blow up early.
160 queue = new ReferenceQueue<Object>();
161 frqRef = new PhantomReference<Object>(this, queue);
162 boolean threadStarted = false;
163 try {
164 startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef);
165 threadStarted = true;
166 } catch (IllegalAccessException impossible) {
167 throw new AssertionError(impossible); // startFinalizer() is public
168 } catch (Throwable t) {
169 logger.log(Level.INFO, "Failed to start reference finalizer thread."
170 + " Reference cleanup will only occur when new references are created.", t);
171 }
172
173 this.threadStarted = threadStarted;
174 }
175
176 @Override
177 public void close() {
178 frqRef.enqueue();
179 cleanUp();
180 }
181
182 /**
183 * Repeatedly dequeues references from the queue and invokes {@link
184 * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a
185 * no-op if the background thread was created successfully.
186 */
187 void cleanUp() {
188 if (threadStarted) {
189 return;
190 }
191
192 Reference<?> reference;
193 while ((reference = queue.poll()) != null) {
194 /*
195 * This is for the benefit of phantom references. Weak and soft references will have already
196 * been cleared by this point.
197 */
198 reference.clear();
199 try {
200 ((FinalizableReference) reference).finalizeReferent();
201 } catch (Throwable t) {
202 logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
203 }
204 }
205 }
206
207 /**
208 * Iterates through the given loaders until it finds one that can load Finalizer.
209 *
210 * @return Finalizer.class
211 */
212 private static Class<?> loadFinalizer(FinalizerLoader... loaders) {
213 for (FinalizerLoader loader : loaders) {
214 Class<?> finalizer = loader.loadFinalizer();
215 if (finalizer != null) {
216 return finalizer;
217 }
218 }
219
220 throw new AssertionError();
221 }
222
223 /**
224 * Loads Finalizer.class.
225 */
226 interface FinalizerLoader {
227
228 /**
229 * Returns Finalizer.class or null if this loader shouldn't or can't load it.
230 *
231 * @throws SecurityException if we don't have the appropriate privileges
232 */
233 @Nullable
234 Class<?> loadFinalizer();
235 }
236
237 /**
238 * Tries to load Finalizer from the system class loader. If Finalizer is in the system class path,
239 * we needn't create a separate loader.
240 */
241 static class SystemLoader implements FinalizerLoader {
242 // This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to disable
243 // finding Finalizer on the system class path even if it is there.
244 @VisibleForTesting
245 static boolean disabled;
246
247 @Override
248 public Class<?> loadFinalizer() {
249 if (disabled) {
250 return null;
251 }
252 ClassLoader systemLoader;
253 try {
254 systemLoader = ClassLoader.getSystemClassLoader();
255 } catch (SecurityException e) {
256 logger.info("Not allowed to access system class loader.");
257 return null;
258 }
259 if (systemLoader != null) {
260 try {
261 return systemLoader.loadClass(FINALIZER_CLASS_NAME);
262 } catch (ClassNotFoundException e) {
263 // Ignore. Finalizer is simply in a child class loader.
264 return null;
265 }
266 } else {
267 return null;
268 }
269 }
270 }
271
272 /**
273 * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to
274 * our class loader (which could be that of a dynamically loaded web application or OSGi bundle),
275 * it would prevent our class loader from getting garbage collected.
276 */
277 static class DecoupledLoader implements FinalizerLoader {
278 private static final String LOADING_ERROR = "Could not load Finalizer in its own class loader."
279 + "Loading Finalizer in the current class loader instead. As a result, you will not be able"
280 + "to garbage collect this class loader. To support reclaiming this class loader, either"
281 + "resolve the underlying issue, or move Google Collections to your system class path.";
282
283 @Override
284 public Class<?> loadFinalizer() {
285 try {
286 /*
287 * We use URLClassLoader because it's the only concrete class loader implementation in the
288 * JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this
289 * class loader:
290 *
291 * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader
292 *
293 * System class loader will (and must) be the parent.
294 */
295 ClassLoader finalizerLoader = newLoader(getBaseUrl());
296 return finalizerLoader.loadClass(FINALIZER_CLASS_NAME);
297 } catch (Exception e) {
298 logger.log(Level.WARNING, LOADING_ERROR, e);
299 return null;
300 }
301 }
302
303 /**
304 * Gets URL for base of path containing Finalizer.class.
305 */
306 URL getBaseUrl() throws IOException {
307 // Find URL pointing to Finalizer.class file.
308 String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class";
309 URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath);
310 if (finalizerUrl == null) {
311 throw new FileNotFoundException(finalizerPath);
312 }
313
314 // Find URL pointing to base of class path.
315 String urlString = finalizerUrl.toString();
316 if (!urlString.endsWith(finalizerPath)) {
317 throw new IOException("Unsupported path style: " + urlString);
318 }
319 urlString = urlString.substring(0, urlString.length() - finalizerPath.length());
320 return new URL(finalizerUrl, urlString);
321 }
322
323 /** Creates a class loader with the given base URL as its classpath. */
324 URLClassLoader newLoader(URL base) {
325 // We use the bootstrap class loader as the parent because Finalizer by design uses
326 // only standard Java classes. That also means that FinalizableReferenceQueueTest
327 // doesn't pick up the wrong version of the Finalizer class.
328 return new URLClassLoader(new URL[] {base}, null);
329 }
330 }
331
332 /**
333 * Loads Finalizer directly using the current class loader. We won't be able to garbage collect
334 * this class loader, but at least the world doesn't end.
335 */
336 static class DirectLoader implements FinalizerLoader {
337 @Override
338 public Class<?> loadFinalizer() {
339 try {
340 return Class.forName(FINALIZER_CLASS_NAME);
341 } catch (ClassNotFoundException e) {
342 throw new AssertionError(e);
343 }
344 }
345 }
346
347 /**
348 * Looks up Finalizer.startFinalizer().
349 */
350 static Method getStartFinalizer(Class<?> finalizer) {
351 try {
352 return finalizer.getMethod(
353 "startFinalizer",
354 Class.class,
355 ReferenceQueue.class,
356 PhantomReference.class);
357 } catch (NoSuchMethodException e) {
358 throw new AssertionError(e);
359 }
360 }
361 }